#ifndef COROUTINE_FIBER_H
#define COROUTINE_FIBER_H

#include <cstdint>
#include <functional>
#include <ucontext.h>
#include <cstddef>
#include <memory>

namespace DBServer {
namespace coroutine
{

/// @brief 协程执行的状态
enum class FiberState
{
    INIT,       // 初始态
    READY,      // 就绪态
    HOLD,       // 挂起态
    EXEC,       // 运行态
    TERM        // 终止态
};

/// 前向声明调度器
class Scheduler;

/// @brief 协程
class Fiber : public std::enable_shared_from_this<Fiber>
{
friend class Scheduler;
public:
    using ptr = std::shared_ptr<Fiber>;

    /// @brief 用户创建一个协程
    /// @param cb_ 协程执行的函数
    /// @param is_schedule 协程是否参与调度器调度 
    Fiber(std::function<void()> cb_, bool is_schedule_ = false);

    /// @brief 恢复协程的执行
    void resume();

    /// @brief 重置协程
    /// @param cb_ 协程的执行的函数
    void reset(std::function<void()> cb_, bool is_schedule = false);

    /// @brief 重置协程的状态
    void set_state(FiberState state_) { state = state_; }

    ~Fiber();

    /// @brief 获取协程的状态
    auto get_state() const -> FiberState { return state; }

    /// @brief 获取协程的id
    auto get_id() const -> std::size_t { return id; }

    /// @brief 协程是否在调度器中被调度
    auto scheduled() const -> bool { return is_scheduled; }

    /// 将协程设置调度
    void in_sche() { is_scheduled = true; }

    /// @brief 取消协程被调度
    void cancel_sche() { is_scheduled = false;}

private:
    /// @brief 创建线程的主协程
    Fiber();

public:
    /// @brief 获取当前协程指针
    static auto GetThis() -> Fiber::ptr;

    /// @brief 设置当前的协程
    static void SetThis(Fiber *f);

    /// @brief 协程让出cpu
    static void Yield();

    /// @brief 协程执行的主函数
    static void MainFunc();

    /// @brief 获取协程的总数量
    static auto TotalFibers() -> std::uint64_t;
private:
    std::size_t             id = -1;        // 协程id
    FiberState              state;          // 协程执行的状态
    ucontext_t              ctx;            // 协程的上下文
    void                    *sk = nullptr;  // 协程栈
    std::function<void()>   cb;             // 协程执行的函数
    bool                    is_scheduled;   // 协程是否参与调度器调度
};

}   // coroutine   
}   // DBServer

#endif // !COROUTINE_FIBER_H